import { delay } from "../lib";

export type CalculateOperatorType = '+' | '-' | '*' | '/' | '=';

const OP_LEVEL: Record<CalculateOperatorType, number> = {
  '=': 0,
  '+': 10,
  '-': 10,
  '*': 100,
  '/': 100,
}

type FnOperatorCalculateType = (a:number, b:number) => number;
const Function: Record<CalculateOperatorType, FnOperatorCalculateType> = {
  '+': (a, b) => a + b,
  '-': (a, b) => a - b,
  '*': (a, b) => a * b,
  '/': (a, b) => a / b,
  '=': () => (0),
}

export type StackItemType = {
  value?: number;
  func?: CalculateOperatorType;
}

type ProcessorStatusType = 'ready' | 'done' | 'processing';
export class Processor {
  public _rawStack: StackItemType[];
  public _rsltStack: StackItemType[];
  public status: ProcessorStatusType;
  public index: number;
  public opLevel: number;

  constructor() {
    this._rawStack = [];
    this._rsltStack = [];
    this.status = 'ready';
    this.index = 0;
    this.opLevel = 0;
  }

  calculate () {
    const pt = this._rsltStack.length;
    if(pt < 3) return 0;

    const f = this._rsltStack[pt - 2].func;
    const a = this._rsltStack[pt - 3].value;
    const b = this._rsltStack[pt - 1].value;
    const value = !f ? 0 : Function[f]?.(a ?? 0, b ?? 0) ?? 0;

    this._rsltStack.pop();
    this._rsltStack.pop();
    this._rsltStack.pop();

    const size = this._rsltStack.length;
    if (size > 1) {
      const fc = this._rsltStack[size - 1].func;
      this.opLevel = !fc ? 0 : OP_LEVEL[fc];
    } else {
      this.opLevel = 0;
    }
    this._rsltStack.push({ value });
  }

  async reduce1Step () {
    // console.log('reduce1Step', this.index)

    if (this.status !== 'processing') {
      this.status = 'processing';
    }

    if (this.index >= this._rawStack.length) {
      while(this._rsltStack.length > 1) {
        this.calculate();
        await delay(1000);
      }
      this.status = 'done';
      return;
    }

    const currentItem = this._rawStack[this.index];
    if (!currentItem.func) {
      this._rsltStack.push({ value: currentItem.value });
      this.index++;
    } else {
      if (this.opLevel < OP_LEVEL[currentItem.func]) {
        this.opLevel = OP_LEVEL[currentItem.func];
        this._rsltStack.push({ func: currentItem.func });
        this.index++;
      } else {
        this.calculate();
      }
    }

  }

}
